﻿#include "stdafx.h"
#pragma comment(lib,"ole32.lib")
#ifdef LK_WINDOWS_WAS_SOUNDENVIRONMENT_
namespace LK
{
	namespace Windows
	{
		namespace WAS
		{
			using namespace ::LK::Sound;
			//SoundEnvironment类
			Mutex SoundEnvironment::soundtc;
			//录音设备集合
			IMMDeviceCollection* SoundEnvironment::pRecordingDevices;
			//播放设备集合
			IMMDeviceCollection* SoundEnvironment::pSpeakingDevices;
			//设备枚举器
			IMMDeviceEnumerator* SoundEnvironment::pEnumerator = NULL;
			//产生异步的设备更改过程
			DeviceChangeCallback* SoundEnvironment::pNotify = NULL;

			//记录已有的扬声器，以便环境变化时发出通知
			LK::Collection::DefaultArray<Speaker*> SoundEnvironment::speakerList;
			//记录已有的录音机，以便环境变化时发出通知
			LK::Collection::DefaultArray<Recorder*> SoundEnvironment::recorderList;
#ifdef LK_FUNCTIONAL_ACTION_
			//发生各项事件时执行的委托（事件）
			LK::Functional::PDelegate<void( ptr<String>)> SoundEnvironment::OnDeviceAdded;
			LK::Functional::PDelegate<void( ptr<String>, DWORD)> SoundEnvironment::OnDeviceStateChanged;
			LK::Functional::PDelegate<void( ptr<String>)> SoundEnvironment::OnDeviceRemoved;
			LK::Functional::PDelegate<void( EDataFlow, ERole, ptr<String>)> SoundEnvironment::OnDefaultDeviceChanged;
			LK::Functional::PDelegate<void( ptr<String>, const PROPERTYKEY)> SoundEnvironment::OnPropertyValueChanged;
#endif

			//录音设备名称列表
			const LK::Collection::DefaultArray<ptr<String>> SoundEnvironment::RecordingDevicesList;
			//录音设备ID列表
			const LK::Collection::DefaultArray<ptr<String>> SoundEnvironment::RecordingDevicesIDList;
			//播放设备名称列表
			const LK::Collection::DefaultArray< ptr<String>> SoundEnvironment::SpeakingDevicesList;
			//播放设备ID列表
			const LK::Collection::DefaultArray<ptr<String>> SoundEnvironment::SpeakingDevicesIDList;

			//默认PSpeaker缓冲时间(0.1s)
			ptr<Time> SoundEnvironment::DefaultSpeakerRequestedDuration = 0.1*Time::EverySecond;
			//最小PSpeaker缓冲时间(0.05s)
			ptr<Time> SoundEnvironment::MinimumSpeakerRequestedDuration = 0.05*Time::EverySecond;
			//默认PRecorder缓冲时间(0.1s)
			ptr<Time> SoundEnvironment::DefaultRecorderRequestedDuration = 0.1*Time::EverySecond;
			//最小PRecorder缓冲时间(0.05s)
			ptr<Time> SoundEnvironment::MinimumRecorderRequestedDuration = 0.05*Time::EverySecond;
			//最后一次更改时已添加的设备ID
			ptr<String> SoundEnvironment::OnAddedDeviceId;
			//最后一次状态更改的设备ID
			ptr<String> SoundEnvironment::OnStateChangedDeviceId;
			//最后一次状态更改到的新状态
			DWORD SoundEnvironment::OnStateChangedNewState;
			//最后一次移除的设备ID
			ptr<String> SoundEnvironment::OnRemovedDeviceId;
			//最后一次默认设备更改的设备ID
			ptr<String> SoundEnvironment::OnDefaultChangedDeviceId;
			//最后一次默认设备更改的设备流向
			EDataFlow SoundEnvironment::OnDefaultChangedFlow;
			//最后一次默认设备更改的设备角色
			ERole SoundEnvironment::OnDefaultChangedRole;
			//最后一次设备属性更改的设备ID
			ptr<String> SoundEnvironment::OnPropertyValueChangedDeviceId;
			//最后一次设备属性更改的设备属性 
			PROPERTYKEY SoundEnvironment::OnPropertyValueChangedKey;
			//设备更改次数
			volatile long SoundEnvironment::changedCount = 0;
			SoundEnvironment::SoundEnvironment()
			{
				CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_SPEED_OVER_MEMORY);
				//初始化
			}
			SoundEnvironment::~SoundEnvironment()
			{
				Clear();
				if (pSpeakingDevices)pSpeakingDevices->Release();
				if (pRecordingDevices)pRecordingDevices->Release();
				if (pEnumerator)
				{
					if (pNotify)pEnumerator->UnregisterEndpointNotificationCallback(pNotify);
					delete pNotify;
					pNotify = NULL;
					pEnumerator->Release();
					pEnumerator = NULL;
				}
			}
			void SoundEnvironment::AddSpeaker(Speaker* ps)
			{
				soundtc.Lock();
				speakerList.Append(ps);
				soundtc.UnLock();
			}
			void SoundEnvironment::AddRecorder(Recorder* pr)
			{
				soundtc.Lock();
				recorderList.Append(pr);
				soundtc.UnLock();
			}
			void SoundEnvironment::RemoveSpeaker(Speaker* ps)
			{
				soundtc.Lock();
				index_t k = speakerList.IndexOf(ps);
				if (k >= 0)
				{
					speakerList[k] = speakerList[speakerList.Length - 1];
					speakerList.Resize(speakerList.Length - 1);
				}
				soundtc.UnLock();
			}
			void SoundEnvironment::RemoveRecorder(Recorder* pr)
			{
				soundtc.Lock();
				index_t k = recorderList.IndexOf(pr);
				if (k >= 0)
				{
					recorderList[k] = recorderList[recorderList.Length - 1];
					recorderList.Resize(recorderList.Length - 1);
				}
				soundtc.UnLock();
			}
			IMMDevice *SoundEnvironment::GetDefaultRecordingDevice()
			{
				IMMDevice *pdev = NULL;
				soundtc.Lock();
				if (!pEnumerator)Refresh();
				if (pEnumerator)pEnumerator->GetDefaultAudioEndpoint(eCapture, eConsole, &pdev);
				soundtc.UnLock();
				return pdev;
			}
			IMMDevice *SoundEnvironment::GetDefaultSpeakingDevice()
			{
				IMMDevice *pdev = NULL;
				soundtc.Lock();
				if (!pEnumerator)Refresh();
				if (pEnumerator)pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pdev);
				soundtc.UnLock();
				return pdev;
			}
			ptr<String> SoundEnvironment::GetDefaultRecordingDeviceId()
			{
				IMMDevice *pdev = NULL;
				soundtc.Lock();
				if (!pEnumerator)Refresh();
				if (pEnumerator)pEnumerator->GetDefaultAudioEndpoint(eCapture, eConsole, &pdev);
				LPWSTR pwstr;
				pdev->GetId(&pwstr);
				ptr<String> s = RecordingDevicesIDList[RecordingDevicesIDList.IndexOf(pwstr)];
				CoTaskMemFree(pwstr);
				pdev->Release();
				soundtc.UnLock();
				return s;
			}
			ptr<String> SoundEnvironment::GetDefaultSpeakingDeviceId()
			{
				IMMDevice *pdev = NULL;
				soundtc.Lock();
				if (!pEnumerator)Refresh();
				if (pEnumerator)pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pdev);
				LPWSTR pwstr;
				pdev->GetId(&pwstr);
				ptr<String> s = SpeakingDevicesIDList[SpeakingDevicesIDList.IndexOf(pwstr)];
				CoTaskMemFree(pwstr);
				pdev->Release();
				soundtc.UnLock();
				return s;
			}
			IMMDevice *SoundEnvironment::GetDevice(const ptr<String>&id)
			{
				IMMDevice *pdev(NULL);
				soundtc.Lock();
				if (pEnumerator)pEnumerator->GetDevice(id, &pdev);
				soundtc.UnLock();
				return pdev;
			}
			ptr<String> SoundEnvironment::GetDeviceID(IMMDevice* pDevice)
			{
				if (!pDevice)return ptr<String>::GetEmpty();
				soundtc.Lock();
				if (!pEnumerator)Refresh();
				LPWSTR tmp;
				SetState(pDevice->GetId(&tmp));//获取id
				ptr<String> s;
				index_t i = SpeakingDevicesIDList.IndexOf(tmp);
				if (i < 0)
				{
					i = RecordingDevicesIDList.IndexOf(tmp);
					if (i < 0)s = tmp;
					else s = RecordingDevicesIDList[i];
				}
				else s = SpeakingDevicesIDList[i];
				CoTaskMemFree(tmp);
				soundtc.UnLock();
				return s;
			}
			ptr<String> SoundEnvironment::GetDeviceName(IMMDevice* pDevice)
			{
				if (!pDevice)return ptr<String>::GetEmpty();
				soundtc.Lock();
				if (!pEnumerator)Refresh();
				HRESULT hr;
				IPropertyStore * p;
				SetState(pDevice->OpenPropertyStore(STGM_READ, &p));//获取信息
				PROPVARIANT inf;
				if (FAILED(hr = p->GetValue(PKEY_Device_FriendlyName, &inf)))//获取名称信息
				{
					p->Release();
					SetState(hr);
				}
				ptr<String> s;
				index_t i = SpeakingDevicesList.IndexOf(inf.pwszVal);
				if (i < 0)
				{
					i = RecordingDevicesList.IndexOf(inf.pwszVal);
					if (i < 0)s = inf.pwszVal;
					else s = RecordingDevicesList[i];
				}
				else s = SpeakingDevicesList[i];
				p->Release();
				soundtc.UnLock();
				return s;
			}

			ptr<LK::Collection::IArray<PSpeaker>> SoundEnvironment::GetSpeakersList()
			{
				soundtc.Lock();
				ptr<LK::Collection::IArray<PSpeaker>>tmp(speakerList.Length);
				//此处需要使用重载的&运算符
				for (index_t i = 0; i < speakerList.Length; ++i)tmp[i] = speakerList[i] ? &(*speakerList[i]) : NULL;
				soundtc.UnLock();
				return tmp;
			}
			ptr<LK::Collection::IArray<PRecorder>> SoundEnvironment::GetRecordersList()
			{
				soundtc.Lock();
				ptr<LK::Collection::IArray<PRecorder>>tmp(recorderList.Length);
				//此处需要使用重载的&运算符
				for (index_t i = 0; i < recorderList.Length; ++i)tmp[i] = recorderList[i] ? &(*recorderList[i]) : NULL;
				soundtc.UnLock();
				return tmp;
			}
			index_t SoundEnvironment::GetChangedCount(){ return changedCount; }//获取更改次数
			void SoundEnvironment::Clear()
			{
				soundtc.Lock();
				const_cast<LK::Collection::DefaultArray<ptr<String>>&>(RecordingDevicesList).Resize(0);
				const_cast<LK::Collection::DefaultArray<ptr<String>>&>(SpeakingDevicesList).Resize(0);
				const_cast<LK::Collection::DefaultArray<ptr<String>>&>(RecordingDevicesIDList).Resize(0);
				const_cast<LK::Collection::DefaultArray<ptr<String>>&>(SpeakingDevicesIDList).Resize(0);
				soundtc.UnLock();
			}
			void SoundEnvironment::Refresh()
			{
				soundtc.Lock();
				if (!pEnumerator)
				{
					//创建设备枚举器
					if (SUCCEEDED(SetState(CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pEnumerator))))
					{
						if (pNotify)pEnumerator->UnregisterEndpointNotificationCallback(pNotify);
						pNotify = new DeviceChangeCallback;
						SetState(pEnumerator->RegisterEndpointNotificationCallback(pNotify));//注册回调类
						SetState(pEnumerator->EnumAudioEndpoints(eCapture, DEVICE_STATEMASK_ALL, &pRecordingDevices));//录音设备
						SetState(pEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATEMASK_ALL, &pSpeakingDevices));//播放设备
					}
				}
				if (pEnumerator)
				{
					LPWSTR pwszID = NULL;//临时字符串
					IMMDevice  *device = NULL;//具体设备
					IPropertyStore *pProps = NULL;
					PROPVARIANT varName;//共用变量转换
					UINT cRD, cSD;//设备数量
					//成功获取设备信息，先删除录音设备描述列表：
					pRecordingDevices->GetCount(&cRD);
					pSpeakingDevices->GetCount(&cSD);
					const_cast<LK::Collection::DefaultArray<ptr<String>>&>(RecordingDevicesList).Resize(cRD);
					const_cast<LK::Collection::DefaultArray<ptr<String>>&>(SpeakingDevicesList).Resize(cSD);
					const_cast<LK::Collection::DefaultArray<ptr<String>>&>(RecordingDevicesIDList).Resize(cRD);
					const_cast<LK::Collection::DefaultArray<ptr<String>>&>(SpeakingDevicesIDList).Resize(cSD);

					if (cRD)
					{
						UINT count(cRD);
						while (count)//获取设备ID列表和名称列表
						{
							count--;
							pRecordingDevices->Item(count, &device);//取设备到device
							device->GetId(&pwszID);//取设备id
							const_cast<LK::Collection::DefaultArray<ptr<String>>&>(RecordingDevicesIDList)[count] = pwszID;//记录id
							CoTaskMemFree(pwszID);
							device->OpenPropertyStore(STGM_READ, &pProps);//取设备描述
							pProps->GetValue(PKEY_Device_FriendlyName, &varName);//取设备名
							const_cast<LK::Collection::DefaultArray<ptr<String>>&>(RecordingDevicesList)[count] = varName.pwszVal;//写入
							pProps->Release();//释放
							device->Release();//释放
						}
					}
					if (cSD)
					{
						UINT count(cSD);
						while (count)//获取设备ID列表和名称列表
						{
							count--;
							pSpeakingDevices->Item(count, &device);//取设备到device
							device->GetId(&pwszID);//取设备id
							const_cast<LK::Collection::DefaultArray<ptr<String>>&>(SpeakingDevicesIDList)[count] = pwszID;//记录id
							CoTaskMemFree(pwszID);
							device->OpenPropertyStore(STGM_READ, &pProps);//取设备描述
							pProps->GetValue(PKEY_Device_FriendlyName, &varName);//取设备名
							const_cast<LK::Collection::DefaultArray<ptr<String>>&>(SpeakingDevicesList)[count] = varName.pwszVal;//写入
							pProps->Release();//释放
							device->Release();//释放
						}
					}
				}
				soundtc.UnLock();
			}
			HRESULT SoundEnvironment::SetState(HRESULT hr)//出错设置
			{
				if (FAILED(hr))Throw <PSoundException >(hr);
				return hr;
			}
			index_t SoundEnvironment::GetDevicesCount()
			{
				if (!pEnumerator)Refresh();
				return SpeakingDevicesList.Length + RecordingDevicesList.Length;
			}
			ptr<String> SoundEnvironment::GetDeviceID(const ptr<String>& name)
			{
				soundtc.Lock();
				if (!pEnumerator)Refresh();
				ptr<String> s = ptr<String>::GetEmpty();
				index_t i = SpeakingDevicesList.IndexOf(name);
				if (i >= 0)
				{
					if (i < SpeakingDevicesIDList.Length)s = SpeakingDevicesIDList[i];
				}
				else
				{
					i = RecordingDevicesList.IndexOf(name);
					if (i >= 0 && i < RecordingDevicesIDList.Length)s = RecordingDevicesIDList[i];
				}
				soundtc.UnLock();
				return s;
			}
			ptr<String> SoundEnvironment::GetDeviceName(const ptr<String>& id)
			{
				soundtc.Lock();
				if (!pEnumerator)Refresh();
				ptr<String> s = ptr<String>::GetEmpty();
				index_t i = SpeakingDevicesIDList.IndexOf(id);
				if (i >= 0)
				{
					if (i < SpeakingDevicesList.Length)s = SpeakingDevicesList[i];
				}
				else
				{
					i = RecordingDevicesIDList.IndexOf(id);
					if (i >= 0 && i < RecordingDevicesList.Length)s = RecordingDevicesList[i];
				}
				soundtc.UnLock();
				return s;
			}
			SoundEnvironment& SoundEnvironment::GetDefaultRef()
			{
				static SoundEnvironment* p;
				if (p)return *p;
				Mutex::GetDefaultRef().Lock();
				if (!p)
				{
					static SoundEnvironment obj;
					p = &obj;
				}
				Mutex::GetDefaultRef().UnLock();
				return *p;
			}
		}
	}
}
#endif
